const { parse } = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const generate = require("@babel/generator").default;
const type = require("@babel/types");
const core = require("@babel/core");
const loaderUtils = require("loader-utils");

// 提供默认的loader option
const DEFAULT_OPTIONS = {
  catchCode: (identifier) => `console.error(${identifier})`,
  identifier: "error",
  finallyCode: null,
};

/**
 * source: js源文件
 */
function tryCatchLoader(source) {
  let options = loaderUtils.getOptions(this);
  options = {
    ...DEFAULT_OPTIONS,
    ...options,
  };

  // 表示是否传入了 catch 块内部的代码
  if (typeof options.catchCode === "function") {
    options.catchCode = options.catchCode(options.identifier);
  }

  // 定义即将添加的 try-catch-finally 代码块中的代码
  let catchNode = parse(options.catchCode).program.body;
  let finallyNode =
    options.finallyCode && parse(options.finallyCode).program.body;

  // 1. 生成AST
  let ast = parse(source, {
    sourceType: "module", // 支持 es6 module
    plugins: ["dynamicImport"], // 支持动态 import
  });

  // 2. 遍历AST， 对 AST进行更改
  traverse(ast, {
    // 3. 在语法树中锁定 await 表达式类型
    AwaitExpression: function (path) {
      // 4. 通过 while 递归向上查找 async function 的节点，修改AST
      while (path && path.node) {
        let parentPath = path.parentPath;
        // 4.1 确认 await的顶层 async 函数节点
        let isAwaitParentNode =
          type.isBlockStatement(path.node) && isAsyncFuncNode(parentPath.node);
        // 4.2 是否已经有 try-catch
        let hasTryCatch =
          type.isBlockStatement(path.node) &&
          type.isTryStatement(parentPath.node);

        if (isAwaitParentNode) {
          // 4.3 构建新的AST
          let tryCatchAst = type.tryStatement(
            path.node,
            type.catchClause(
              type.identifier(options.identifier),
              type.blockStatement(catchNode)
            ),
            finallyNode && type.blockStatement(finallyNode)
          );
          path.replaceWithMultiple([tryCatchAst]);
          break;
        } else if (hasTryCatch) {
          break;
        }
        // 循环的变量
        path = parentPath;
      }
    },
    CallExpression(path) {
      // 检查调用表达式的 callee 是否是 console.log
      if (
        isConsoleLogNode(path.node)
      ) {
        // const argument = path.node.arguments[0];
        const code = generate(path.node).code
        // if (type.isLiteral(argument)) {
        //   // 参数是字面量
        //   const newArgument = type.stringLiteral(`'普通字面量': `);
        //   addPreTip(newArgument, path.node.arguments)
        // } else if (type.isIdentifier(argument)) {
        //   // 参数是一个原始变量
        //   const newArgument = type.stringLiteral(`${path.node.arguments[0].name}: `);
        //   addPreTip(newArgument, path.node.arguments)
        // } else {
        //   // 参数可能是其他类型的表达式，例如函数调用、数组、对象等
        //   const code = calleeCode.code
        //   const arg = code.slice(12, -1)
        //   const newArgument = type.stringLiteral(`${arg}: `);
        //   addPreTip(newArgument, path.node.arguments)
        // }
        const arg = code.slice(12, -1)
        const newArgument = type.stringLiteral(`${arg}:\n`);
        addPreTip(newArgument, path.node.arguments)

      }
    },

  });

  // 5. 给定新的 AST , 并转换
  return core.transformFromAstSync(ast, null, {
    configFile: false,
  }).code;
}

/**
 * @param AST Node
 */
function isAsyncFuncNode(node) {
  const res =
    type.isFunctionDeclaration(node, {
      // 函数声明
      async: true,
    }) ||
    type.isArrowFunctionExpression(node, {
      //箭头函数
      async: true,
    }) ||
    type.isFunctionExpression(node, {
      // 函数表达式
      async: true,
    }) ||
    type.isObjectMethod(node, {
      // 对象的方法
      async: true,
    });
  return res;
}


function isConsoleLogNode(node) {
  const res = type.isMemberExpression(node.callee) &&
    type.isIdentifier(node.callee.object, { name: "console" }) &&
    type.isIdentifier(node.callee.property, { name: "log" })
  return res
}

function addPreTip(tipLiteral, argument) {
  argument.unshift(tipLiteral)
}

module.exports = tryCatchLoader;
